#!/bin/sh

# Test cases for exists.sh

# Copyright 2019-2020, 2022 orbea
# All rights reserved.
#
# Redistribution and use of this script, with or without modification, is
# permitted provided that the following conditions are met:
#
# 1. Redistributions of this script must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#
#  THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
#  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
#  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO
#  EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
#  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
#  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
#  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
#  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
#  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
#  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

IFS=' 	
'
\unset -f command printf unalias 2>/dev/null
\unalias -a 2>/dev/null
PATH="$(command -p getconf PATH):$PATH"
LC_ALL=C; export LC_ALL

set -euf

COMMAND=
FULLRUN=
TESTSHELL=
VAR=
VERBOSE=
WRAPPER=

usage='test.sh - Test framework for exists.sh.

  Usage: test.sh [OPTIONS]
    -f, --full, Run all tests including very slow tests.
    -h, --help, Show this help message.
    -s, --shell, Path of the shell.
    -v, --verbose, Verbose logging.
    -w, --wrapper, Select tests to run.

  Supported tests:
    command, exists (default), which'

chkscript () {
  case "$1" in
    command|exists|which ) WRAPPER="$1" ;;
    * ) die 1 "Unrecognized script '$1.sh', use -h for help." ;;
  esac
}

chkvar () {
  case "$1" in
    '' ) die 1 "Unrecognized option '$2', use -h for help." ;;
    WRAPPER ) chkscript "$2"; VAR= ;;
    * ) eval "$VAR=\"$2\""; VAR= ;;
  esac
}

die () {
  ret="$1"; shift
  case "$ret" in
    : ) printf %s\\n "$@" >&2; return 0 ;;
    0 ) printf %s\\n "$@" ;;
    * ) printf %s\\n "$@" >&2 ;;
  esac
  exit "$ret"
}

for flag do
  case "$flag" in
    '' ) : ;;
    -- ) break ;;
    -f|--full ) FULLRUN=1 ;;
    -h|--help ) die 0 "$usage" ;;
    -s|--shell ) VAR=TESTSHELL ;;
    -v|--verbose ) VERBOSE=1; VAR= ;;
    -w|--wrapper ) VAR=WRAPPER ;;
    -* ) die 1 "Unrecognized option '$flag', use -h for help." ;;
    * ) chkvar "$VAR" "$flag" ;;
  esac
done

if [ ! -x "${TESTSHELL:=/bin/sh}" ] || [ ! -f "$TESTSHELL" ]; then
  die 1 "ERROR: Shell '$TESTSHELL' is not an executable file."
fi

checkfile () {
  [ -z "$skip" ] || return 0

  fail=0
  v=2

  # Some shells do not have a builtin printf and $PATH is unset
  printf_hack "${NEWPATH:=tests}"

  case "$NEWPATH" in
    __empty__ ) PATH= ;;
    __unset__ ) unset PATH ;;
    * ) PATH="$NEWPATH" ;;
  esac

  if [ -n "${1+x}" ]; then
    CMD="$("$TESTSHELL" -- "$rootdir"/$WRAPPER.sh "${@:-}")" || fail=1
  else
    CMD="$("$TESTSHELL" -- "$rootdir"/$WRAPPER.sh)" || fail=1
  fi

  export PATH="$ORIGPATH"
  NEWPATH=

  if [ $fail -lt 1 ] && [ "${CMD}" = "${test:-}" ]; then
    v=0
  elif [ $fail -gt 0 ] && [ "${CMD}" = "${test:-}" ]; then
    v=1
  fi

  log "output = $CMD" "match  = ${test:-}" "input  = ${@:-}" "result = $fail"

  set +f
  rm -rf -- tests/*
  set -f
}

makefile () {
  [ -z "$skip" ] || return 0
  while [ $# -gt 0 ]; do
    file="${1%:*}"
    perm="${1##*:}"
    shift
    [ "$file" = "${file%/*}" ] || mkdir -p "tests/${file%/*}"
    touch "tests/$file"
    chmod "$perm" "tests/$file"
    if [ -x "tests/$file" ] && [ -f "tests/$file" ]; then
      printf %s\\n "$rootdir/tests/$file"
    fi
  done
}

printf_hack () {
  case "$1" in
    __empty__|__unset__ ) p=':.' ;;
    * ) p=":$1" ;;
  esac
  while [ "$p" != "${p%:*}" ]; do
    dir="${p##*:}"
    if [ -d "${dir#:}" ]; then
      cp -- "$rootdir"/scripts/printf "${p##*:}/"
      chmod 0755 "${p##*:}/printf"
      break
    fi
    p="${p%:*}"
  done
}

result () {
  case "$1" in
    0 ) result=PASS ;;
    1 ) result=FAIL ;;
  esac

  if [ -n "$skip" ]; then
    printf %s\\n "SKIP - $2 || $result" >&2
    shift 2
    [ -z "${1+x}" ] || log "$@" >&2
  elif [ "$v" = "$1" ]; then
      printf %s\\n "PASS - $2 || $result"
  else
      printf %s\\n "FAIL - $2 || $result" >&2
  fi
}

settest () { test="$(printf %s\\n "$@")"; }
quit () { err=$?; rm -f ./printf; rm -rf ./tests/; }

case "$VERBOSE" in
  1 ) log () { printf %s\\n "$@"; } ;;
  * ) log () { :; } ;;
esac

# Test if the command builtin accepts multiple arguments with -v
if [ "$WRAPPER" = command ] && "$TESTSHELL" scripts/command-args-test.sh; then
  COMMAND=1
  # Test if the command builtin exits on the first missing executable with -v
  if "$TESTSHELL" scripts/command-args2-test.sh; then
    COMMAND=2
  fi
fi

# HACK for broken shells like posh and Slackware ash.
if "$TESTSHELL" scripts/pwd-test.sh "$(pwd)" >/dev/null 2>&1; then
  retpwd () { pwd; }
else
  retpwd () { pwd -P; }
fi

if [ "$0" != "${0%/*}" ]; then
  rootdir="$(cd -- "${0%/*}/" && retpwd)"
  cd -- "$rootdir"
else
  rootdir="$(retpwd)"
fi

NEWPATH=
ORIGPATH="$PATH"
WRAPPER="${WRAPPER:-exists}"

#########################
#### Shell bug tests ####
#########################

skip=

SKIP_EMPTY_PATH=
SKIP_UNSET_PATH=

# Test if an empty PATH variable is replaced
# This happens with ksh93-20191228_5abcbd06 in Slackware
if PATH= "$TESTSHELL" scripts/path-empty-test.sh; then
  SKIP_EMPTY_PATH=1
fi

# Test if an unset PATH variable is replaced
# This happens with all shells other than yash
unset PATH
if "$TESTSHELL" scripts/path-empty-test.sh; then
  SKIP_UNSET_PATH=1
fi
export PATH="$ORIGPATH"

##########################
#### Tests start here ####
##########################

rm -rf tests
trap 'quit; trap - EXIT; exit $err' EXIT INT
mkdir tests

# test an executable file - pass
(
  test="$(makefile testfile:0755)"
  checkfile testfile
  result 0 'Executable File'
)

# test a non-executable file - fail
(
  test="$(makefile testfile:0644)"
  checkfile testfile
  result 1 'Non-executable File'
)

# test $0 as an executable file - pass
(
  test="$rootdir/$0"
  NEWPATH="$rootdir"
  checkfile "$0"
  result 0 "\$0 Executable File"
)

# test an executable file with a trailing forward slash - fail
(
  test="$(makefile testfile:0755)"
  test=
  checkfile testfile/
  result 1 'Executable File with a Trailing Forward Slash'
)

# test an non-executable file with a trailing forward slash - fail
(
  test="$(makefile testfile:0644)"
  checkfile testfile/
  result 1 'Non-executable File with a Trailing Forward Slash'
)

# test an executable file with a colon - pass
(
  test="$(makefile test:file:0755)"
  checkfile test:file
  result 0 'Executable File with a Colon'
)

# test an executable file with a space - pass
(
  test="$(makefile test\ file:0755)"
  checkfile test\ file
  result 0 'Executable File with a Space'
)

# test an executable file with a backslash - pass
(
  test="$(makefile test\\file:0755)"
  checkfile test\\file
  result 0 'Executable File with a Backslash'
)

# test an executable file with a dollar sign - pass
(
  test="$(makefile test\$file:0755)"
  checkfile test\$file
  result 0 'Executable File with a Dollar Sign'
)

# test an executable file with a grave - pass
(
  test="$(makefile test\`file:0755)"
  checkfile test\`file
  result 0 'Executable File with a Grave'
)

# test an executable file with a wildcard sign - pass
(
  test="$(makefile test\*file:0755)"
  checkfile test\*file
  result 0 'Executable File with a Wildcard Sign - 1'
)

(
  test="$(makefile test\*file:0755 test1file:0755)"
  settest "$rootdir"/tests/test\*file
  checkfile test\*file
  result 0 'Executable File with a Wildcard Sign - 2'
)

(
  test="$(makefile test\*file:0755 test1file:0755)"
  settest "$rootdir"/tests/test\*file
  cd tests/
  NEWPATH="$rootdir/tests"
  checkfile test\*file
  result 0 'Executable File with a Wildcard Sign - 3'
)

# test an executable file with a single quote - pass
(
  test="$(makefile test\'file:0755)"
  checkfile test\'file
  result 0 'Executable File with a Single Quote'
)

# test an executable file with a double quote - pass
(
  test="$(makefile test\"file:0755)"
  checkfile test\"file
  result 0 'Executable File with a Double Quote'
)

# test an executable file with mixed quotes - pass
(
  test="$(makefile \"test\'file:0755)"
  checkfile \"test\'file
  result 0 'Executable File with Mixed Quotes'
)

# test an executable file as a dash - pass
(
  test="$(makefile -:0755)"
  checkfile -- -
  result 0 'Executable File as a Dash - 1'
)

(
  test="$(makefile --:0755)"
  checkfile -- --
  result 0 'Executable File as a Dash - 2'
)

(
  test="$(makefile ---:0755)"
  checkfile -- ---
  result 0 'Executable File as a Dash - 3'
)

# test an executable file prefixed with a dash - pass
(
  test="$(makefile -testfile:0755)"
  checkfile -- -testfile
  result 0 'Executable File Prefixed with a Dash - 1'
)

(
  test="$(makefile --testfile:0755)"
  checkfile -- --testfile
  result 0 'Executable File Prefixed with a Dash - 2'
)

(
  test="$(makefile ---testfile:0755)"
  checkfile -- ---testfile
  result 0 'Executable File Prefixed with a Dash - 3'
)

# test an executable file as a symlink - pass
(
  test="$(makefile testfile1:0755)"
  settest "$rootdir"/tests/testfile2
  ln -s "$rootdir/tests/testfile1" "$rootdir/tests/testfile2"
  checkfile testfile2
  result 0 'Executable File as as Symlink'
)

# test and non-executable file as a symlink - fail
(
  test="$(makefile testfile1:0644)"
  ln -s "$rootdir/tests/testfile1" "$rootdir/tests/testfile2"
  checkfile testfile2
  result 1 'Non-executable File as as Symlink'
)

# test an absolute file path - pass
(
  test="$(makefile testfile:0755)"
  checkfile "$rootdir/tests/testfile"
  result 0 'Absolute File Path'
)

# test an absolute file path with a double forward slash - pass
(
  test="$(makefile foobar//testfile:0755)"
  checkfile "$rootdir/tests/foobar//testfile"
  result 0 'Absolute File Path with a Double Forward Slash'
)

# test an absolute file path with a colon - pass
(
  test="$(makefile foo:bar/testfile:0755)"
  checkfile "$rootdir/tests/foo:bar/testfile"
  result 0 'Absolute File Path with a Colon'
)

# test an absolute file path with spaces - pass
(
  test="$(makefile foo\ bar/test\ file:0755)"
  checkfile "$rootdir/tests/foo bar/test file"
  result 0 'Absolute File Path with Spaces'
)

# test a relative file path - pass
(
  test="$(makefile testfile:0755)"
  checkfile tests/testfile
  result 0 'Relative File Path - 1'
)

(
  test="$(makefile testfile:0755)"
  settest "$rootdir"/./tests/testfile
  checkfile ./tests/testfile
  result 0 'Relative File Path - 2'
)

# test a relative file path with a double forward slash - pass
(
  test="$(makefile foobar//testfile:0755)"
  checkfile tests/foobar//testfile
  result 0 'Relative File Path with a Double Forward Slash - 1'
)

(
  test="$(makefile foobar//testfile:0755)"
  settest "$rootdir"/./tests/foobar//testfile
  checkfile ./tests/foobar//testfile
  result 0 'Relative File Path with a Double Forward Slash - 2'
)

# test a relative file path with a colon - pass
(
  test="$(makefile foo:bar/testfile:0755)"
  checkfile tests/foo:bar/testfile
  result 0 'Relative File Path with a Colon - 1'
)

(
  test="$(makefile foo:bar/testfile:0755)"
  settest "$rootdir"/./tests/foo:bar/testfile
  checkfile ./tests/foo:bar/testfile
  result 0 'Relative File Path with a Colon - 2'
)

# test a relative file path with spaces - pass
(
  test="$(makefile foo\ bar/test\ file:0755)"
  checkfile tests/foo\ bar/test\ file
  result 0 'Relative File Path with Spaces - 1'
)

(
  test="$(makefile foo\ bar/test\ file:0755)"
  settest "$rootdir"/./tests/foo\ bar/test\ file
  checkfile ./tests/foo\ bar/test\ file
  result 0 'Relative File Path with Spaces - 2'
)

# test first directory in $PATH - pass
(
  test="$(makefile local/bin/testfile1:0755)"
  NEWPATH=tests/local/bin:tests/bin:tests/usr/bin
  checkfile testfile1
  result 0 "First Directory in \$PATH - 1"
)

(
  test="$(makefile local/bin/testfile1:0755 bin/testfile2:0755 \
    usr/bin/testfile3:0755)"
  settest "$rootdir"/tests/local/bin/testfile1
  NEWPATH=tests/local/bin:tests/bin:tests/usr/bin
  checkfile testfile1
  result 0 "First Directory in \$PATH - 2"
)

# test middle directory in $PATH - pass
(
  test="$(makefile bin/testfile2:0755)"
  NEWPATH=tests/local/bin:tests/bin:tests/usr/bin
  checkfile testfile2
  result 0 "Middle Directory in \$PATH - 1"
)

(
  test="$(makefile local/bin/testfile1:0755 bin/testfile2:0755 \
    usr/bin/testfile3:0755)"
  settest "$rootdir"/tests/bin/testfile2
  NEWPATH=tests/local/bin:tests/bin:tests/usr/bin
  checkfile testfile2
  result 0 "Middle Directory in \$PATH - 2"
)

# test final directory in $PATH - pass
(
  test="$(makefile usr/bin/testfile3:0755)"
  NEWPATH=tests/local/bin:tests/bin:tests/usr/bin
  checkfile testfile3
  result 0 "Final Directory in \$PATH - 1"
)

(
  test="$(makefile local/bin/testfile1:0755 bin/testfile2:0755 \
    usr/bin/testfile3:0755)"
  settest "$rootdir"/tests/usr/bin/testfile3
  NEWPATH=tests/local/bin:tests/bin:tests/usr/bin
  checkfile testfile3
  result 0 "Final Directory in \$PATH - 2"
)

# test matching directory and file names - pass
(
  test="$(makefile tests:0755)"
  checkfile tests
  result 0 'Matching Directory and File Name - Executable'
)

# test matching directory and file names - fail
(
  test="$(makefile tests:0644)"
  checkfile tests
  result 1 'Matching Directory and File Name - Non-Executable'
)

# test a directory - fail
(
  mkdir tests/testdir
  checkfile testdir
  result 1 'Directory is not a File'
)

(
  mkdir tests/tests
  checkfile tests
  result 1 'Directory is not a File - Matching Directory and Path Name'
)

(
  mkdir tests/testdir1 tests/testdir2 tests/testdir3
  checkfile testdir1 testdir2 testdir3
  result 1 'Multiple Directories are not Files'
)

# test a missing file - fail
(
  checkfile testfile
  result 1 'Missing File'
)

# test a wildcard with no files - fail
(
  cd tests/
  set +f
  checkfile *
  set -f
  result 1 'Wildcard Sign with no Files'
)

# test the working directory with an executable file - pass
(
  test="$(makefile testfile:0755)"
  NEWPATH=.
  cd tests
  settest "$rootdir"/tests/./testfile
  checkfile testfile
  result 0 'Working Directory with an Executable File'
)

# test the working directory with an executable file - fail
(
  test="$(makefile testfile:0755)"
  NEWPATH=.
  test=
  checkfile testfile
  result 1 'Working Directory with an Executable File'
)

# test the working directory with a non-executable file - fail
(
  test="$(makefile testfile:0644)"
  NEWPATH=.
  cd tests
  checkfile testfile
  result 1 'Working Directory with a Non-executable File'
)

# test the working directory with an executable absolute file path - pass
(
  test="$(makefile testfile:0755)"
  NEWPATH=.
  cd tests
  checkfile "$rootdir"/tests/testfile
  result 0 'Working Directory with an Executable File Absolute Path - 1'
)

(
  test="$(makefile testfile:0755)"
  NEWPATH=.
  checkfile "$rootdir"/tests/testfile
  result 0 'Working Directory with an Executable File Absolute Path - 2'
)

# test the working directory with an executable relative file path - pass
(
  test="$(makefile testfile:0755)"
  NEWPATH=.
  checkfile tests/testfile
  result 0 'Working Directory with an Executable File Relative Path - 1'
)

(
  test="$(makefile testfile:0755)"
  NEWPATH=.
  cd tests
  settest "$rootdir"/tests/./testfile
  checkfile ./testfile
  result 0 'Working Directory with an Executable File Relative Path - 2'
)

# test the working directory with a non-executable absolute file path - fail
(
  test="$(makefile testfile:0644)"
  NEWPATH=.
  checkfile "$rootdir"/testfile
  result 1 'Working Directory with a Non-executable File Absolute Path - 1'
)

(
  test="$(makefile testfile:0644)"
  NEWPATH=.
  cd tests
  checkfile "$rootdir"/testfile
  result 1 'Working Directory with a Non-executable File Absolute Path - 2'
)

# test the working directory with a non-executable relative file path - fail
(
  test="$(makefile testfile:0644)"
  NEWPATH=.
  checkfile tests/testfile
  result 1 'Working Directory with a Non-executable File Relative Path - 1'
)

(
  test="$(makefile testfile:0644)"
  NEWPATH=.
  cd tests
  checkfile ./testfile
  result 1 'Working Directory with a Non-executable File Relative Path - 2'
)

# test an empty $PATH with an executable file - pass
(
  [ -z "${SKIP_EMPTY_PATH}" ] || skip=1
  test="$(makefile testfile:0755)"
  NEWPATH=__empty__
  cd tests
  checkfile testfile
  result 0 "Empty \$PATH with an Executable File" \
    "With '$TESTSHELL' an empty PATH is helpfully replaced."
)

# test an empty $PATH with an executable file - fail
(
  [ -z "${SKIP_EMPTY_PATH}" ] || skip=1
  test="$(makefile testfile:0755)"
  NEWPATH=__empty__
  test=
  checkfile testfile
  result 1 "Empty \$PATH with an Executable File" \
    "With '$TESTSHELL' an empty PATH is helpfully replaced."
)

# test an empty $PATH with a non-executable file - fail
(
  [ -z "${SKIP_EMPTY_PATH}" ] || skip=1
  test="$(makefile testfile:0644)"
  NEWPATH=__empty__
  cd tests
  checkfile testfile
  result 1 "Empty \$PATH with a Non-executable File - 1" \
    "With '$TESTSHELL' an empty PATH is helpfully replaced."
)

(
  [ -z "${SKIP_EMPTY_PATH}" ] || skip=1
  test="$(makefile testfile:0644)"
  NEWPATH=__empty__
  checkfile testfile
  result 1 "Empty \$PATH with a Non-executable File - 2" \
    "With '$TESTSHELL' an empty PATH is helpfully replaced."
)

# test an empty $PATH with an executable absolute file path - pass
(
  [ -z "${SKIP_EMPTY_PATH}" ] || skip=1
  test="$(makefile testfile:0755)"
  NEWPATH=__empty__
  checkfile "$rootdir"/tests/testfile
  result 0 "Empty \$PATH with an Executable File Absolute Path" \
    "With '$TESTSHELL' an empty PATH is helpfully replaced."
)

# test an empty $PATH with an executable relative file path - pass
(
  [ -z "${SKIP_EMPTY_PATH}" ] || skip=1
  test="$(makefile testfile:0755)"
  NEWPATH=__empty__
  checkfile tests/testfile
  result 0 "Empty \$PATH with an Executable File Relative Path - 1" \
    "With '$TESTSHELL' an empty PATH is helpfully replaced."
)

(
  [ -z "${SKIP_EMPTY_PATH}" ] || skip=1
  test="$(makefile testfile:0755)"
  NEWPATH=__empty__
  cd tests
  settest "$rootdir"/tests/./testfile
  checkfile ./testfile
  result 0 "Empty \$PATH with an Executable File Relative Path - 2" \
    "With '$TESTSHELL' an empty PATH is helpfully replaced."
)

# test an empty $PATH with a non-executable absolute file path - fail
(
  [ -z "${SKIP_EMPTY_PATH}" ] || skip=1
  test="$(makefile testfile:0644)"
  NEWPATH=__empty__
  checkfile "$rootdir"/testfile
  result 1 "Empty \$PATH with a Non-executable File Absolute Path" \
    "With '$TESTSHELL' an empty PATH is helpfully replaced."
)

# test an empty $PATH with a non-executable relative file path - fail
(
  [ -z "${SKIP_EMPTY_PATH}" ] || skip=1
  test="$(makefile testfile:0644)"
  NEWPATH=__empty__
  checkfile tests/testfile
  result 1 "Empty \$PATH with a Non-executable File Relative Path - 1" \
    "With '$TESTSHELL' an empty PATH is helpfully replaced."
)

(
  [ -z "${SKIP_EMPTY_PATH}" ] || skip=1
  test="$(makefile testfile:0644)"
  NEWPATH=__empty__
  cd tests
  checkfile ./testfile
  result 1 "Empty \$PATH with a Non-executable File Relative Path - 2" \
    "With '$TESTSHELL' an empty PATH is helpfully replaced."
)

# test an unset $PATH with an executable file - fail
(
  [ -z "${SKIP_UNSET_PATH}" ] || skip=1
  test="$(makefile testfile:0755)"
  NEWPATH=__unset__
  cd tests
  test=
  checkfile testfile
  result 1 "Unset \$PATH with an Executable File - 1" \
    "With '$TESTSHELL' an unset PATH is helpfully replaced."
)

# test an unset $PATH with an executable file - fail
(
  [ -z "${SKIP_UNSET_PATH}" ] || skip=1
  test="$(makefile testfile:0755)"
  NEWPATH=__unset__
  test=
  checkfile testfile
  result 1 "Unset \$PATH with an Executable File - 2" \
    "With '$TESTSHELL' an unset PATH is helpfully replaced."
)

# test an unset $PATH with a non-executable file - fail
(
  [ -z "${SKIP_UNSET_PATH}" ] || skip=1
  test="$(makefile testfile:0644)"
  NEWPATH=__unset__
  cd tests
  checkfile testfile
  result 1 "Unset \$PATH with a Non-executable File - 1" \
    "With '$TESTSHELL' an unset PATH is helpfully replaced."
)

(
  [ -z "${SKIP_UNSET_PATH}" ] || skip=1
  test="$(makefile testfile:0644)"
  NEWPATH=__unset__
  checkfile testfile
  result 1 "Unset \$PATH with a Non-executable File - 2" \
    "With '$TESTSHELL' an unset PATH is helpfully replaced."
)

# test an unset $PATH with an executable absolute file path - pass
(
  [ -z "${SKIP_UNSET_PATH}" ] || skip=1
  test="$(makefile testfile:0755)"
  NEWPATH=__unset__
  checkfile "$rootdir"/tests/testfile
  result 0 "Unset \$PATH with an Executable File Absolute Path" \
    "With '$TESTSHELL' an unset PATH is helpfully replaced."
)

# test an unset $PATH with an executable relative file path - pass
(
  [ -z "${SKIP_UNSET_PATH}" ] || skip=1
  test="$(makefile testfile:0755)"
  NEWPATH=__unset__
  checkfile tests/testfile
  result 0 "Unset \$PATH with an Executable File Relative Path - 1" \
    "With '$TESTSHELL' an unset PATH is helpfully replaced."
)

(
  [ -z "${SKIP_UNSET_PATH}" ] || skip=1
  test="$(makefile testfile:0755)"
  NEWPATH=__unset__
  cd tests
  settest "$rootdir"/tests/./testfile
  checkfile ./testfile
  result 0 "Unset \$PATH with an Executable File Relative Path - 2" \
    "With '$TESTSHELL' an unset PATH is helpfully replaced."
)

# test an unset $PATH with a non-executable absolute file path - fail
(
  [ -z "${SKIP_UNSET_PATH}" ] || skip=1
  test="$(makefile testfile:0644)"
  NEWPATH=__unset__
  checkfile "$rootdir"/testfile
  result 1 "Unset \$PATH with a Non-executable File Absolute Path" \
    "With '$TESTSHELL' an unset PATH is helpfully replaced."
)

# test an unset $PATH with a non-executable relative file path - fail
(
  [ -z "${SKIP_UNSET_PATH}" ] || skip=1
  test="$(makefile testfile:0644)"
  NEWPATH=__unset__
  checkfile tests/testfile
  result 1 "Unset \$PATH with a Non-executable File Relative Path - 1" \
    "With '$TESTSHELL' an unset PATH is helpfully replaced."
)

(
  [ -z "${SKIP_UNSET_PATH}" ] || skip=1
  test="$(makefile testfile:0644)"
  NEWPATH=__unset__
  cd tests
  checkfile ./testfile
  result 1 "Unset \$PATH with a Non-executable File Relative Path - 2" \
    "With '$TESTSHELL' an unset PATH is helpfully replaced."
)

# test an empty argument - fail
(
  checkfile ''
  result 1 'Empty Argument'
)

# test an unset argument - pass
(
  checkfile
  result 0 'Unset Argument'
)

if [ "$WRAPPER" = exists ] || [ "$WRAPPER" = which ] || [ -n "$COMMAND" ]; then
  # test multiple executable files - pass
  (
    test="$(makefile testfile1:0755 testfile2:0755)"
    checkfile testfile1 testfile2
    result 0 'Multiple Executable Files - 1'
  )

  (
    test="$(makefile testfile1:0755 testfile2:0755 testfile3:0755)"
    checkfile testfile1 testfile2 testfile3
    result 0 'Multiple Executable Files - 2'
  )

  # test multiple non-executable files - pass
  (
    test="$(makefile testfile1:0644 testfile2:0644)"
    checkfile testfile1 testfile2
    result 1 'Multiple Non-executable Files - 1'
  )

  (
    test="$(makefile testfile1:0644 testfile2:0644 testfile3:0644)"
    checkfile testfile1 testfile2 testfile3
    result 1 'Multiple Non-executable Files - 2'
  )

  # test multiple missing files - fail
  (
    checkfile testfile1 testfile2
    result 1 'Multiple Missing Files - 1'
  )

  (
    checkfile testfile1 testfile2 testfile3
    result 1 'Multiple Missing Files - 2'
  )

  # test multiple executable and non-executable files - fail
  (
    test="$(makefile testfile1:0755 testfile2:0644)"
    settest "$rootdir"/tests/testfile1
    checkfile testfile1 testfile2
    result 1 'Multiple Executable and Non-Executable Files - 1'
  )

  (
    test="$(makefile testfile1:0755 testfile2:0755 testfile3:0644)"
    settest "$rootdir"/tests/testfile1 "$rootdir"/tests/testfile2
    checkfile testfile1 testfile2 testfile3
    result 1 'Multiple Executable and Non-Executable Files - 2'
  )

  # test an absolute file path with a wildcard - pass
  (
    test="$(makefile testfile1:0755 testfile2:0755 testfile3:0755)"
    settest "$rootdir/tests/testfile1" "$rootdir/tests/testfile2" \
      "$rootdir/tests/testfile3"
    set +f
    checkfile "$rootdir"/tests/test*
    set -f
    result 0 'Absolute File Path with a Wildcard'
  )

  # test multiple files and directories - fail
  (
    test="$(makefile testfile1:0755 testfile2:0755)"
    mkdir tests/testdir1 tests/testdir2
    settest "$rootdir"/tests/testfile1 "$rootdir"/tests/testfile2
    checkfile testfile1 testfile2 testdir1 testdir2
    result 1 'Multiple Executable Files and Directories'
  )

  # test multiple present and missing files - fail
  (
    test="$(makefile testfile1:0755)"
    checkfile testfile1 testfile2
    result 1 'Multiple Present and Missing Files - 1'
  )

  (
    test="$(makefile testfile1:0755 testfile2:0755)"
    checkfile testfile1 testfile2 testfile3
    result 1 'Multiple Present and Missing Files - 2'
  )

  (
    test="$(makefile testfile1:0755 testfile2:0755 testfile3:0755)"
    checkfile testfile1 testfile2 testfile3 testifile4
    result 1 'Multiple Present and Missing Files - 3'
  )

  if [ "$WRAPPER" != command ] || [ "$COMMAND" = 2 ]; then
    (
      test="$(makefile testfile2:0755 testfile3:0755)"
      checkfile testfile1 testfile2 testfile3
      result 1 'Multiple Present and Missing Files - 4'
    )

    (
      test="$(makefile testfile1:0755 testfile3:0755)"
      checkfile testfile1 testfile2 testfile3
      result 1 'Multiple Present and Missing Files - 5'
    )

    # test multiple files and directories - fail
    (
      test="$(makefile testfile1:0644 testfile2:0755)"
      mkdir tests/testdir1 tests/testdir2
      settest "$rootdir"/tests/testfile2
      checkfile testfile1 testfile2 testdir1 testdir2
      result 1 'Multiple Executable Files, Non-executable Files and Directories'
    )

    # test multiple files in the $PATH with a wildcard - fail
    (
      test="$(makefile testfile1:0644 testfile2:0644 bin/testfile1:0755 \
        usr/bin/testfile2:0755)"
      settest "$rootdir"/tests/bin/testfile1 "$rootdir"/tests/usr/bin/testfile2
      cd tests/
      NEWPATH=bin:usr/bin
      set +f
      checkfile *
      set -f
      result 1 "Multiple Executable Files in the \$PATH with a Wildcard"
    )

    # test multiple files with dashes - fail
    (
      test="$(makefile --:0755 -testfile:0644 -:0755 --testfile:0755 ---:0644 \
        ---testfile:0755)"
      settest "$rootdir"/tests/-- "$rootdir"/tests/- "$rootdir"/tests/--testfile \
        "$rootdir"/tests/---testfile
      checkfile -- -- -testfile - --testfile --- ---testfile
      result 1 'Multiple files with dashes - 1'
    )

    (
      test="$(makefile --:0644 -testfile:0755 -:0644 --testfile:0644 ---:0755 \
        ---testfile:0644)"
      settest "$rootdir"/tests/-testfile "$rootdir"/tests/---
      checkfile -- -- -testfile - --testfile --- ---testfile
      result 1 'Multiple files with dashes - 2'
    )

    (
      test="$(makefile -testfile:0755 ---:0644 --testfile:0644 --:0755 \
        ---testfile:0755 -:0755)"
      settest "$rootdir"/tests/-testfile "$rootdir"/tests/-- \
        "$rootdir"/tests/---testfile "$rootdir"/tests/-
      checkfile -- -testfile --- --testfile -- ---testfile -
      result 1 'Multiple files with dashes - 3'
    )
  fi

  # test 100 executable files - pass
  (
    files1=
    files2=
    for i in $(seq 100); do
      files1="${files1} $i"
      files2="${files2} $i:0755"
    done
    eval "set -- $files2"
    test="$(makefile "$@")"
    eval "set -- $files1"
    checkfile "$@"
    result 0 '100 Executable Files'
  )

  # Very slow tests
  if [ "$FULLRUN" = 1 ]; then
    # test 1000 executable files - pass
    (
      files1=
      files2=
      for i in $(seq 1000); do
        files1="${files1} $i"
        files2="${files2} $i:0755"
      done
      eval "set -- $files2"
      test="$(makefile "$@")"
      eval "set -- $files1"
      checkfile "$@"
      result 0 '1000 Executable Files'
    )

    # test 10000 executable files - pass
    (
      files1=
      files2=
      for i in $(seq 10000); do
        files1="${files1} $i"
        files2="${files2} $i:0755"
      done
      eval "set -- $files2"
      test="$(makefile "$@")"
      eval "set -- $files1"
      checkfile "$@"
      result 0 '10000 Executable Files'
    )
  fi
fi

if [ "$WRAPPER" = exists ] || [ "$WRAPPER" = which ]; then
  # test --all - pass
  (
    test="$(makefile bin/testfile:0755 usr/bin/testfile:0755)"
    NEWPATH=tests/local/bin:tests/bin:tests/usr/bin
    checkfile --all testfile
    result 0 'All matches with --all and executable files - 1'
  )

  (
    test="$(makefile bin/testfile:0755 usr/bin/testfile:0755)"
    NEWPATH=tests/local/bin:tests/bin:tests/usr/bin
    checkfile -a testfile
    result 0 'All matches with --all and executable files - 2'
  )

  (
    test="$(makefile local/bin/testfile:0755 bin/testfile:0644 \
      usr/bin/testfile:0755)"
    settest "$rootdir"/tests/local/bin/testfile \
      "$rootdir"/tests/usr/bin/testfile
    NEWPATH=tests/local/bin:tests/bin:tests/usr/bin
    checkfile --all testfile
    result 0 'All matches with --all and executable files - 3'
  )

  (
    test="$(makefile local/bin/testfile:0755 bin/testfile:0644 \
      usr/bin/testfile:0755)"
    settest "$rootdir"/tests/local/bin/testfile \
      "$rootdir"/tests/usr/bin/testfile
    NEWPATH=tests/local/bin:tests/bin:tests/usr/bin
    checkfile -a testfile
    result 0 'All matches with --all and executable files - 4'
  )

  # test --all with non-exuectubable files - fail
  (
    test="$(makefile bin/testfile:0644 usr/bin/testfile:0644)"
    NEWPATH=tests/local/bin:tests/bin:tests/usr/bin
    checkfile --all testfile
    result 1 'All matches with --all and non-executable files - 1'
  )

  (
    test="$(makefile bin/testfile:0644 usr/bin/testfile:0644)"
    NEWPATH=tests/local/bin:tests/bin:tests/usr/bin
    checkfile -a testfile
    result 1 'All matches with --all and non-executable files - 2'
  )

  # test executable files titled --all - pass
  (
    test="$(makefile --all:0755)"
    checkfile -- --all
    result 0 'Executable file titled --all - 1'
  )

  (
    test="$(makefile -a:0755)"
    checkfile -- -a
    result 0 'Executable file titled --all - 2'
  )

  (
    test="$(makefile bin/--all:0755 usr/bin/--all:0755)"
    NEWPATH=tests/local/bin:tests/bin:tests/usr/bin
    checkfile --all -- --all
    result 0 'Executable file titled --all - 3'
  )

  (
    test="$(makefile bin/-a:0755 usr/bin/-a:0755)"
    NEWPATH=tests/local/bin:tests/bin:tests/usr/bin
    checkfile -a -- -a
    result 0 'Executable file titled --all - 4'
  )

  (
    test="$(makefile bin/--all:0755 usr/bin/--all:0755)"
    NEWPATH=tests/local/bin:tests/bin:tests/usr/bin
    checkfile -a -- --all
    result 0 'Executable file titled --all - 5'
  )

  (
    test="$(makefile bin/-a:0755 usr/bin/-a:0755)"
    NEWPATH=tests/local/bin:tests/bin:tests/usr/bin
    checkfile --all -- -a
    result 0 'Executable file titled --all - 6'
  )

  # test non-executable files titled --all - fail
  (
    test="$(makefile --all:0644)"
    checkfile -- --all
    result 1 'Non-executable file titled --all - 1'
  )

  (
    test="$(makefile -a:0644)"
    checkfile -- -a
    result 1 'Non-executable file titled --all - 2'
  )

  (
    test="$(makefile bin/--all:0644 usr/bin/--all:0644)"
    NEWPATH=tests/local/bin:tests/bin:tests/usr/bin
    checkfile --all -- --all
    result 1 'Non-executable file titled --all - 3'
  )

  (
    test="$(makefile bin/-a:0644 usr/bin/-a:0644)"
    NEWPATH=tests/local/bin:tests/bin:tests/usr/bin
    checkfile -a -- -a
    result 1 'Non-executable file titled --all - 4'
  )

  (
    test="$(makefile bin/--all:0644 usr/bin/--all:0644)"
    NEWPATH=tests/local/bin:tests/bin:tests/usr/bin
    checkfile -a -- --all
    result 1 'Non-executable file titled --all - 5'
  )

  (
    test="$(makefile bin/-a:0644 usr/bin/-a:0644)"
    NEWPATH=tests/local/bin:tests/bin:tests/usr/bin
    checkfile --all -- -a
    result 1 'Non-executable file titled --all - 6'
  )

  # Invalid command-line with --all - pass
  (
    test="$(makefile --all:0755)"
    test=
    checkfile --all
    result 0 'Invalid command-line with --all - 1'
  )

  (
    test="$(makefile -a:0755)"
    test=
    checkfile -a
    result 0 'Invalid command-line with --all - 2'
  )

  (
    test="$(makefile bin/--all:0755 usr/bin/--all:0755)"
    NEWPATH=tests/local/bin:tests/bin:tests/usr/bin
    test=
    checkfile --all --all
    result 0 'Invalid command-line with --all - 3'
  )

  (
    test="$(makefile bin/-a:0755 usr/bin/-a:0755)"
    NEWPATH=tests/local/bin:tests/bin:tests/usr/bin
    test=
    checkfile -a -a
    result 0 'Invalid command-line with --all - 4'
  )

  (
    test="$(makefile bin/--all:0755 usr/bin/--all:0755)"
    NEWPATH=tests/local/bin:tests/bin:tests/usr/bin
    test=
    checkfile -a --all
    result 0 'Invalid command-line with --all - 5'
  )

  (
    test="$(makefile bin/-a:0755 usr/bin/-a:0755)"
    NEWPATH=tests/local/bin:tests/bin:tests/usr/bin
    test=
    checkfile --all -a
    result 0 'Invalid command-line with --all - 6'
  )

  # test --all at the end of the commandline - pass
  (
    test="$(makefile bin/-a:0755 bin/testfile:0755 usr/bin/testfile:0755)"
    settest "$rootdir"/tests/bin/testfile "$rootdir"/tests/bin/-a
    NEWPATH=tests/local/bin:tests/bin:tests/usr/bin
    checkfile testfile -a
    result 0 'Executable files with --all at the end of the command-line - 1'
  )

  (
    test="$(makefile bin/--all:0755 bin/testfile:0755 usr/bin/testfile:0755)"
    settest "$rootdir"/tests/bin/testfile "$rootdir"/tests/bin/--all
    NEWPATH=tests/local/bin:tests/bin:tests/usr/bin
    checkfile testfile --all
    result 0 'Executable files with --all at the end of the command-line - 2'
  )

  # test --all at the end of the commandline - fail
  (
    test="$(makefile bin/testfile:0755 usr/bin/testfile:0755)"
    settest "$rootdir"/tests/bin/testfile
    NEWPATH=tests/local/bin:tests/bin:tests/usr/bin
    checkfile testfile -a
    result 1 'Executable files with --all at the end of the command-line - 3'
  )

  (
    test="$(makefile bin/testfile:0755 usr/bin/testfile:0755)"
    settest "$rootdir"/tests/bin/testfile
    NEWPATH=tests/local/bin:tests/bin:tests/usr/bin
    checkfile testfile --all
    result 1 'Executable files with --all at the end of the command-line - 4'
  )
fi
